# 底层
import datetime
import os
import random
from threading import Thread
import time
import inspect
import ctypes

# 视觉
# import DeepSort.track as track
import cv2 as cv
from PySide6 import QtCore
from PySide6.QtCore import QUrl, Signal, Qt
from PySide6.QtGui import QImage, QPixmap, QMouseEvent
from PySide6.QtWebEngineCore import QWebEngineSettings
from PySide6.QtWebEngineWidgets import QWebEngineView
import ocr

# UI界面
from PySide6.QtWidgets import *

# 通信
from IoT import APP
from StreamProcessor import VideoProcessor, MapProcessor
from coordinate import Coordinate
from database import DBTool
from ui_control import Ui_USV
from visualData import EnvData
import telecom


# -------------------桌面程序------------------
class MainUSV(QWidget, Ui_USV):
    def __init__(self):
        super(MainUSV, self).__init__()
        self.setupUi(self)

        # 地图显示子窗口
        self.map_window = MapWindow()

        # 实例化更新控件
        self.updater = UpdateObject()
        self.updater.update_signal.connect(self.web_update)  # updater 信号函数 连接 更新函数

        # 设置图表外观属性
        web_geometry = self.ph_web.geometry()
        self.ph_web = QWebEngineView(self.ph_web)
        self.ph_web.setGeometry(web_geometry)
        self.ph_web.settings().setAttribute(QWebEngineSettings.ShowScrollBars, False)

        self.tds_web = QWebEngineView(self.tds_web)
        self.tds_web.setGeometry(web_geometry)
        self.tds_web.settings().setAttribute(QWebEngineSettings.ShowScrollBars, False)

        self.temp_web = QWebEngineView(self.temp_web)
        self.temp_web.setGeometry(web_geometry)
        self.temp_web.settings().setAttribute(QWebEngineSettings.ShowScrollBars, False)

        self.ship_web = QWebEngineView(self.ship_web)
        self.ship_web.setGeometry(web_geometry)
        self.ship_web.settings().setAttribute(QWebEngineSettings.ShowScrollBars, False)

        self.fish_web = QWebEngineView(self.fish_web)
        self.fish_web.setGeometry(web_geometry)
        self.fish_web.settings().setAttribute(QWebEngineSettings.ShowScrollBars, False)

        self.cam.mousePressEvent = self.cam_click
        self.map.mousePressEvent = self.map_click
        self.map_window.origin_map.mousePressEvent = self.map_click

        self.euler.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.move_condition.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.quaternions.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        # 类初始化
        self.env = EnvData()
        # self.ocrer = ChiOcr()
        self.IoTquester = APP()
        self.videoprocessor = VideoProcessor()
        self.mapprocessor = MapProcessor()
        self.commes = telecom.TelecomAction('127.0.0.1')
        self.camCoordinate = Coordinate()
        self.tool = DBTool()

        # 将信号连接与方法连接
        self.forward.clicked.connect(self.go_forward)
        self.backward.clicked.connect(self.go_backward)
        self.left.clicked.connect(self.turn_left)
        self.right.clicked.connect(self.turn_right)
        self.stop.clicked.connect(self.set_stop)
        self.backButton.clicked.connect(self.control_back)
        self.recognizeButton.clicked.connect(self.control_recognize)
        self.ocrButton.clicked.connect(self.control_ocr)
        self.mapButton.clicked.connect(self.control_map)
        self.armButton.clicked.connect(self.control_arm)
        self.videoButton.clicked.connect(self.control_video)
        self.OriginButton.clicked.connect(self.show_map_window)

    @staticmethod
    def cam_click(ev: QMouseEvent):
        if ev.button() == Qt.MouseButton.LeftButton:
            usv.camCoordinate.px = ev.pos().x()
            usv.camCoordinate.py = ev.pos().y()
            x, y, z = usv.camCoordinate.pixtoworld(usv.camCoordinate.px, usv.camCoordinate.py)
            print(x, y, z)

    @staticmethod
    def map_click(ev: QMouseEvent):
        print(ev.pos())

    def go_forward(self):
        speed = random.random()
        forward = (''.join(['forward', '|', str(speed)])).encode()
        self.commes.send_action(forward)
        print('forward')

    def go_backward(self):
        speed = random.random()
        backward = (''.join(['backward', '|', str(speed)])).encode()
        self.commes.send_action(backward)
        print('backward')

    def turn_left(self):
        speed = random.random()
        left = (''.join(['left', '|', str(speed)])).encode()
        self.commes.send_action(left)
        print('left')

    def turn_right(self):
        speed = random.random()
        right = (''.join(['right', '|', str(speed)])).encode()
        self.commes.send_action(right)
        print('right')

    def set_stop(self):
        stop = 'stop|'.encode()
        self.commes.send_action(stop)
        print('stop')

    def set_direction(self):
        angle = random.random()
        direction = (''.join(['direction', '|', str(angle)])).encode()
        self.commes.send_action(direction)
        print('direction')

    @staticmethod
    def control_arm():
        pass

    @staticmethod
    def control_video():
        stop_thread(capture)
        stop_thread(th1)
        stop_thread(th2)
        stop_thread(th3)

    @staticmethod
    def control_ocr():
        stop_thread(ocr)

    @staticmethod
    def control_recognize():
        # stop_thread(detect)
        pass

    @staticmethod
    def control_back():
        usv.commes.send_action('back'.encode())
        print('goes back')

    @staticmethod
    def control_map():
        stop_thread(map_update)
        stop_thread(map_receive)

    def show_map_window(self):
        self.map_window.show()

    # 重载html更新图表
    def web_update(self):
        self.ph_web.load(QUrl('file:///charts/pH.html'))
        self.tds_web.load(QUrl('file:///charts/tds.html'))
        self.temp_web.load(QUrl('file:///charts/temp.html'))
        self.ship_web.load(QUrl('file:///charts/boat.html'))
        self.fish_web.load(QUrl('file:///charts/fish.html'))

    def format_table(self):
        # 设置文本居中
        for i in range(1):
            for j in range(4):
                self.move_condition.item(i, j).setTextAlignment(QtCore.Qt.AlignCenter)
                self.quaternions.item(i, j).setTextAlignment(QtCore.Qt.AlignCenter)

        for i in range(1):
            for j in range(3):
                try:
                    self.euler.item(i, j).setTextAlignment(QtCore.Qt.AlignCenter)
                except AttributeError:
                    pass

        self.tdsTable.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
        self.temTable.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)
        self.phTable.item(0, 0).setTextAlignment(QtCore.Qt.AlignCenter)


# 地图子窗口
class MapWindow(QWidget):
    def __init__(self):
        super(MapWindow, self).__init__(parent=None)
        self.setWindowTitle("原始比例")
        self.resize((QtCore.QSize(640, 640)))
        self.origin_map = QLabel(self)


# 子线程更新用自定义控件
class UpdateObject(QtCore.QObject):
    # 创建信号
    update_signal = Signal()

    def __init__(self):
        super(UpdateObject, self).__init__()

    # 调用run以激活信号
    def run(self):
        self.update_signal.emit()


# 子线程运行run
def update_process(update: UpdateObject):
    while True:
        usv.env.gene_temp()
        usv.env.gene_ph()
        usv.env.gene_tds()
        usv.env.gene_boat()
        usv.env.gene_fish()

        update.run()
        time.sleep(1)


def get_current_data_loop(environment: EnvData, requester: APP):  # theading模块多线程获取环境数据
    while True:
        # 获取属性值
        try:
            tds_value, tds_time = requester.queryDeviceData("tds")
            tem_value, tem_time = requester.queryDeviceData("temperature")
            ph_value, ph_time = requester.queryDeviceData("ph")
            fish_value, fish_time = requester.queryDeviceData("fish")
            boat_value, boat_time = requester.queryDeviceData("boat")
            mile_value, mile_time = requester.queryDeviceData("mileage")
            speed_value, spped_time = requester.queryDeviceData("speed")
            acceleration_value, acceleration_time = requester.queryDeviceData("acceleration")
            endurance_value, enduance_time = requester.queryDeviceData("endurance")
            yaw_value = requester.queryDeviceData("yaw")[0]
            pitch_value = requester.queryDeviceData("pitch")[0]
            roll_value = requester.queryDeviceData("roll")[0]
        except TypeError:
            continue
        # 更新到本地数据
        tem_time = time.strftime('%H:%M:%S', time.localtime(int(tem_time[:10])))
        ph_time = time.strftime('%H:%M:%S', time.localtime(int(ph_time[:10])))
        tds_time = time.strftime('%H:%M:%S', time.localtime(int(tds_time[:10])))
        fish_time = time.strftime('%H:%M:%S', time.localtime(int(fish_time[:10])))
        boat_time = time.strftime('%H:%M:%S', time.localtime(int(boat_time[:10])))

        try:
            environment.temperature.update({tem_time: float(tem_value)})
            environment.ph.update({ph_time: float(ph_value)})
            environment.tds.update({tds_time: float(tds_value)})
            environment.density_fish.update({fish_time: float(fish_value)})
            environment.density_ship.update({boat_time: float(boat_value)})
        except ValueError:
            continue

        # 格式化属性值
        tds_item = QTableWidgetItem("".join(str(tds_value) + "mg/L"))
        tem_item = QTableWidgetItem("".join(str(tem_value) + "℃"))
        ph_item = QTableWidgetItem(ph_value)
        mile_item = QTableWidgetItem("".join(str(mile_value) + "km"))
        speed_item = QTableWidgetItem("".join(str(speed_value) + "m/s"))
        acceleration_item = QTableWidgetItem("".join(str(acceleration_value) + "m/s²"))
        endurance_item = QTableWidgetItem("".join(str(endurance_value) + "%"))
        yaw_item = QTableWidgetItem("".join(str(yaw_value) + "°"))
        pitch_item = QTableWidgetItem("".join(str(pitch_value) + "°"))
        roll_item = QTableWidgetItem("".join(str(roll_value) + "°"))

        # 在table控件中显示
        usv.tdsTable.setItem(0, 0, tds_item)
        usv.temTable.setItem(0, 0, tem_item)
        usv.phTable.setItem(0, 0, ph_item)
        usv.move_condition.setItem(0, 0, mile_item)
        usv.move_condition.setItem(0, 1, speed_item)
        usv.move_condition.setItem(0, 2, acceleration_item)
        usv.move_condition.setItem(0, 3, endurance_item)
        usv.euler.setItem(0, 0, yaw_item)
        usv.euler.setItem(0, 1, pitch_item)
        usv.euler.setItem(0, 2, roll_item)


def request_from_database():
    while True:
        navigate_data = usv.tool.select_navigate()[0][1:]
        for identifier in usv.env.dict_env:
            dataset = reversed(usv.tool.select(identifier))
            dict_temp = {}
            for data in dataset:
                hms, value = data[0], data[1]
                hms = datetime.date.__format__(hms, '%H:%M:%S')
                dict_temp.update({hms: value})
            usv.env.__setattr__(identifier, dict_temp)

        for index, identifier in enumerate(usv.env.dict_navigate):
            usv.env.__setattr__(identifier, navigate_data[index])

        tds_item = QTableWidgetItem("".join(str(usv.env.tds) + "mg/L"))
        tem_item = QTableWidgetItem("".join(str(usv.env.temperature) + "℃"))
        ph_item = QTableWidgetItem(str(usv.env.ph))
        mile_item = QTableWidgetItem("".join(str(usv.env.mileage) + "km"))
        speed_item = QTableWidgetItem("".join(str(usv.env.speed) + "m/s"))
        acceleration_item = QTableWidgetItem("".join(str(usv.env.accelerate) + "m/s²"))
        endurance_item = QTableWidgetItem("".join(str(usv.env.endurance) + "%"))
        yaw_item = QTableWidgetItem("".join(str(usv.env.yaw) + "°"))
        pitch_item = QTableWidgetItem("".join(str(usv.env.pitch) + "°"))
        roll_item = QTableWidgetItem("".join(str(usv.env.roll) + "°"))
        x_item = QTableWidgetItem(str(usv.env.X))
        y_item = QTableWidgetItem(str(usv.env.Y))
        z_item = QTableWidgetItem(str(usv.env.Z))
        w_item = QTableWidgetItem(str(usv.env.W))

        # 在table控件中显示
        usv.tdsTable.setItem(0, 0, tds_item)
        usv.temTable.setItem(0, 0, tem_item)
        usv.phTable.setItem(0, 0, ph_item)
        usv.move_condition.setItem(0, 0, mile_item)
        usv.move_condition.setItem(0, 1, speed_item)
        usv.move_condition.setItem(0, 2, acceleration_item)
        usv.move_condition.setItem(0, 3, endurance_item)
        usv.euler.setItem(0, 0, yaw_item)
        usv.euler.setItem(0, 1, pitch_item)
        usv.euler.setItem(0, 2, roll_item)
        usv.quaternions.setItem(0, 0, x_item)
        usv.quaternions.setItem(0, 1, y_item)
        usv.quaternions.setItem(0, 2, z_item)
        usv.quaternions.setItem(0, 3, w_item)

        usv.format_table()
        time.sleep(1)


# ---------视频捕捉处理--------------
# 将numpy类型图形处理后在label中显示
def video_show(processor: VideoProcessor):
    while True:
        if len(processor.img) != 0:
            frame = processor.img[0]
            frame = cv.cvtColor(frame, cv.COLOR_RGB2BGR)
            img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
            usv.cam.setPixmap(QPixmap.fromImage(img))
            del processor.img[0]


def map_show(processor: MapProcessor):
    while True:
        map_data = processor.tool.MapConvert(processor.map_model, (64, 64))
        map_data = cv.resize(map_data, (640, 640))
        map_data = cv.cvtColor(map_data, cv.COLOR_RGB2BGR)
        processor.img = QImage(map_data.data, map_data.shape[1], map_data.shape[0], QImage.Format_RGB888)
        usv.map.setPixmap(QPixmap.fromImage(processor.img))
        usv.map_window.origin_map.setPixmap(QPixmap.fromImage(processor.img))


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


if __name__ == "__main__":
    app = QApplication()
    usv = MainUSV()
    usv.show()

    # 图像接收线程
    th1, th2, th3 = usv.videoprocessor.video_thread()

    # 捕捉图像
    capture = Thread(target=video_show, args=[usv.videoprocessor])
    capture.start()

    # 连接信号更新UI
    web_update = Thread(target=update_process, args=[usv.updater])
    web_update.start()

    # 地图接收线程
    map_receive = Thread(target=usv.mapprocessor.map_receive)
    map_receive.start()

    # 地图更新线程
    map_update = Thread(target=map_show, args=[usv.mapprocessor])
    map_update.start()

    # 文字识别
    # ocr = Thread(target=ocr.ChiOcr.ocr, args=[usv.videoprocessor])
    # ocr.start()

    # 目标识别跟踪
    # detect = Thread(target=track.run, args=[usv.videoprocessor])
    # detect.start()

    visual = Thread(target=request_from_database, args=[])
    visual.start()

    os._exit(app.exec())
